package client.xmpp;
/*
 * Author: Maris
 * 
 * Description: - Connects to server with specified username/pass, 
 * 				sends messages to specified target.
 * 				- Sends XMPP Ping packets to server (XEP-099)
 * 
 * http://community.igniterealtime.org/thread/26681
 * http://www.igniterealtime.org/builds/smack/docs/latest/documentation/
 * http://www.javacodegeeks.com/2010/09/xmpp-im-with-smack-for-java.html
 * 
 * Note: class is singleton, as per http://www.javabeginner.com/learn-java/java-singleton-design-pattern
 */

import java.util.Collection;
import java.util.HashMap;
import java.util.Scanner;

import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Presence;

import util.pref.*;

public class XMPPClient {
    private static XmppManager xmppManager;
	private Thread pingerthread;
	HashMap<String,String> hm;
	String configfile=null;
	private static XMPPClient xmppclientobject;	
	
	/**
     * Implementation of the singleton pattern using lazy instantiation.
	*/

	/**
	 * Private constructor to prevent instantiation from other classes. Conforms
	 * to the singleton design pattern.
	 */
	private XMPPClient(String configfile) {
		this.configfile = configfile;
	}

    private XMPPClient() {
    }

	/**
	 * synchronized is used for thread-safety reasons.
	 * 
	 * @return an instance of the {@link XMPPClient} class
	 */
    public static synchronized XMPPClient getInstance(String configfile) {
        if (xmppclientobject == null) {
            xmppclientobject = new XMPPClient(configfile);
        }
        return xmppclientobject;
    }
    
    public static synchronized XMPPClient getInstance() {
        if (xmppclientobject == null) {
            xmppclientobject = new XMPPClient();
        }
        return xmppclientobject;
    }

	/**
	 * Prevents cloning of a singleton.
	 * 
	 * @return nothing is returned in order to prevent cloning
	 * @throws CloneNotSupportedException
	 */
    @Override
    public Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }
	
	private static class MyRosterListener implements RosterListener { 
		public void entriesAdded(Collection<String> addresses) {}
	    public void entriesDeleted(Collection<String> addresses) {}
	    public void entriesUpdated(Collection<String> addresses) {}
/**
 * 	on presence change calls gui.updateUserList (Array[index][String[] 
 * 			{ userid, name, status] } ) 
 */
	    @Override
	    public void presenceChanged(Presence presence) {
	    	System.out.println("Presence changed: " + presence.getFrom() + " " + presence);
	    	try {
		    		String[][] rarray = xmppManager.returnRoster();
		    		for ( String[] list : rarray) {
		    			System.out.println(String.format("%1$s<%2$s> %3$s", 
		    					list[1], list[0], list[2]));
		    		}
	    		} catch (Exception e) {
				e.printStackTrace();
			}
	    }
	}
/** 
 * Sends XMPP ping IQ stanza to server every X seconds (by calling XmppManager.pingServer )
 * @author Maris
 *
 */
	private class PingerThread implements Runnable {
		private boolean doRun=true;
		int timeout=30;		//seconds, default 30
		
		public PingerThread (int time ) {
			timeout = time;
		}
		public void run() {
	        while (doRun==true) {
	        	try {
	        		xmppManager.pingServer();
	        		Thread.sleep(timeout*1000);
	        	} catch (XMPPException e) {
	        		e.printStackTrace();
	        	} catch (InterruptedException e) {
	        		doRun=false;
				}
	        }
	        
	    }
	}
/** 
 * Loads variables, starts client, connects, logs in, starts pinger	
 * @throws Exception
 */
	public void init() throws Exception {

		int 	port 		= 5222;
		int 	pingfrequency = 60;
		boolean debug 		= false;

		String[] s = {"server_address", "username", "password", "debug", "xmpp_server_port",
						"xmpp_server_ping_frequency", "chat_to", "chat_to_name" };
		PrefHandler p = null;
		if (configfile==null) {
			p = new PrefHandler();
		} else {
			p = new PrefHandler(configfile);
		}
		hm = p.getPreferences(s);		

		String 	server 		= hm.get("server_address");
		String	username 	= hm.get("username");
		String 	password 	= hm.get("password");
		String 	propDebug 	= hm.get("debug");
		String	sPort		= hm.get("xmpp_server_port");
		String	sPing		= hm.get("xmpp_server_ping_frequency");
		
		if (username!=null) 	username = 	username.replace('"', ' ');
		if (username!=null) 	username =	username.trim();
		if (password!=null) 	password=	password.replace('"', ' ');
		if (password!=null) 	password=	password.trim();
		if (server!=null) 		server=		server.replace('"', ' ');
		if (server!=null)		server=		server.trim();
		if (propDebug!=null)	debug = 	propDebug.contains("1");
		if (sPort!=null) 		port = 		Integer.parseInt(sPort);
		if (sPing!=null) 		pingfrequency = Integer.parseInt(sPing);
		
		xmppManager = new XmppManager(server, port, debug);
		
		xmppManager.init();

		try { 
			xmppManager.performLogin(username, password);
		} catch (XMPPException e) {
			System.out.println(String.format("Could not login %1$s with password %2$s", username, password));
			xmppManager.destroy();
			System.exit(-1);		
		}
		
		xmppManager.setStatus(true, "Hello everyone"); // true - available

		pingerthread = new Thread(null, new PingerThread(pingfrequency), "PingerThread");	// start pinging server every X seconds
		pingerthread.start();
	}
	
	public void doStuff() throws Exception {
		String buddyJID = hm.get("chat_to");
		String buddyName = hm.get("chat_to_name");

//		xmppManager.createEntry(buddyJID, buddyName);
		
		Roster roster = xmppManager.getRoster();
		roster.addRosterListener(new MyRosterListener());
		
//		xmppManager.printServerVersion();
		
		String text;
		Scanner in = new Scanner(System.in);
		do {
			text = in.nextLine();
			if (text.compareTo("exit")==0) break;
			if (text.length()>0) {
				xmppManager.sendMessage(text, buddyJID);
			}
		} while (true);
		
	}

	public void destroy() {
		pingerthread.interrupt();	
		xmppManager.destroy();
	}
}

